//http://www.therailsway.com/2006/11/16/tracks-part-2
//Class is now called RailsStyleInterceptorLogic.
class X {
  
   def before = ["initEveryTime"]

   def init(){ println 'init'}

   def initEveryTime() {println "everytime"}

   def sayIt(str){return str}

   def sayItTwice(str) {return str + str}

   def sayItThrice(str) {return str + str + str}

}

class SubX{
   def skipBefore = ["initEveryTime"]

   def sayItSub(){println "subX"}

}



def x = new SubX()

x.metaClass.hasProperty(x, "hi")

//TODO  The latest code now supports skipBefore and skipAfter that has a String, 
//along with the List support that appears here.

def handle  = new Handle()
handle.obj = x
handle.methName = "sayIt" 
handle.filterProperty = "before"
handle.skipFilterProperty = "skipBefore"
handle.exe()
assert handle.filtersToInvoke == []


handle.clearIntermediate()
handle.obj = new X()
handle.exe()
assert handle.filtersToInvoke == ["initEveryTime"]

println "!!!!!!!!!!!!!!!!!!!!!!!!"
handle.clearIntermediate()
handle.obj.before << "init"
handle.exe() 

assert handle.filtersToInvoke == ["initEveryTime", "init"]

handle.clearIntermediate()
//Add initBefore to the object we're working on
def emc = new ExpandoMetaClass(handle.obj.class, false)
emc.initBefore = [except : ["sayIt", "sayItTwice"]]
emc.initialize()
handle.obj.metaClass = emc

handle.exe()
assert handle.filtersToInvoke == ["initEveryTime"]

handle.clearIntermediate()
handle.methName = "sayItThrice"
handle.exe()
assert handle.filtersToInvoke == ["initEveryTime", "init"]

handle.clearIntermediate()
handle.obj.initBefore = initBefore = [only : ["sayIt", "sayItTwice"]]
handle.exe()
assert handle.filtersToInvoke == ["initEveryTime"]

//NOW use a new object and try out initBefore as a List, not a HashMap.  Ran into trouble trying to change the 
//initBefore we'd already injected.   

handle.clearObject()
//Now initBefore is no longer a Map, it's a list.  Unfortunately, we can't just 
//set a List into the initBefore property, because Groovy will give us a 
//CastExcepton.
class Try{
   def before = ["initEveryTime", "init"]
   def initBefore = ["sayIt", "sayItTwice"]

   def init(){ println 'init'}
   def initEveryTime() {println "everytime"}

   def sayIt(str){return str}
   def sayItTwice(str) {return str + str}
   def sayItThrice(str) {return str + str + str}

}

def well = new Try()
handle.obj = well;
handle.methName = "sayItThrice"
handle.exe()
assert handle.filtersToInvoke == ["initEveryTime"]

handle.clearIntermediate()
handle.obj.initBefore << "sayItThrice"
handle.exe()
assert handle.filtersToInvoke == ["initEveryTime", "init"]





 class Handle{
       public static final String EXCEPT = "except"
       public static final String ONLY = "only"
       def filterProperty //Name of the property that holds the filter information
       def skipFilterProperty     
       def obj
       def methName

       def filters  //intermediate
       def skipThese

       def filtersToInvoke = []//result

       def clearIntermediate(){
           filters = null
           skipThese = null
           filtersToInvoke = []
       }

      def clearObject(){
         clearIntermediate()
         obj = null
         methName = null
      }

     def clear(){
        clearObject()
        filterProperty = null
        skipFilterProperty = null
        
     }

       def exe(){
            if(! hasProperty(filterProperty))
               return
            filters = obj."$filterProperty"
            iter()
            
       }

      def iter(){
          for(filter in filters){
                println "filter is " + filter
                if(! (filter instanceof String)) throw new RuntimeException("Filter List must be made up entirely of Strings")
                String filterInfoName = filter + filterProperty[0].toUpperCase() + filterProperty.substring(1)
                println "filterInfoName is " + filterInfoName
                def filterInfo = getPropertyOfObj(filterInfoName)
               
                println "filterInfo is " + filterInfo     
                if(filterInfo instanceof Map){
                    boolean ok = handleMapFilterInfo(filterInfo)
                    if(!ok)
                        continue;
                }   

                if(filterInfo instanceof List){ 
                    if(! filterInfo.any{it == methName} )
                       continue;
                }       
   
                if(!shouldSkip(filter))
                   filtersToInvoke << filter
                
          }
      }
     
      def handleMapFilterInfo(map){
           if(map.size() != 1) throw new RuntimeException("Map must be of size 1")
           def foundMethodNameInList
           def exceptOrOnly 
           boolean tf = map.each{key, value ->
               exceptOrOnly = key
               foundMethodNameInList = value.any{it == methName}
           }
           if(exceptOrOnly == EXCEPT)
                return !foundMethodNameInList
           else if(exceptOrOnly == ONLY)
                return foundMethodNameInList
           else 
                throw new RuntimeException("Map key must be either $EXCEPT or $ONLY.  Value of $exceptOrOnly is not permitted.")
      }
      def hasProperty(propName) {
            obj.metaClass.hasProperty(obj, propName) != null
      }

      def getPropertyOfObj(propName){
         return hasProperty(propName) ? obj."$propName" : null
      }

      def shouldSkip(filter){
          if(skipThese == "NONE") return false
          skipThese =  hasProperty("$skipFilterProperty") ? obj."$skipFilterProperty" : "NONE"
          return skipThese.any{ it == filter}
      }
      

}







